package com.mriassl.stream.consumer.service.impl;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.MessageFormat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mriassl.stream.consumer.constant.BillingType;
import com.mriassl.stream.consumer.domain.AccountDO;
import com.mriassl.stream.consumer.domain.BillingDO;
import com.mriassl.stream.consumer.lib.rabbitmq.RabbitMQConst;
import com.mriassl.stream.consumer.mapper.AccountMapper;
import com.mriassl.stream.consumer.mapper.BillingMapper;
import com.mriassl.stream.consumer.service.BillingService;

@Service
public class BillingServiceImpl implements BillingService {

    private final Logger log = LoggerFactory.getLogger(BillingServiceImpl.class);

    @Value("${server.port}")
    private int port;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private BillingMapper billingMapper;

    @Override
    @Transactional
    public void saveBilling(String busno, Long userid, BigDecimal price) {
        log.info(MessageFormat.format("port:{0},保存账务明细", port));
        BillingDO billing = new BillingDO();
        billing.setOrderno(busno);
        int isExists = billingMapper.selectCount(billing);
        if(isExists > 0) {
            rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_FOR_BETTING, RabbitMQConst.ROUTE_FOR_SUCC_BETTING_REPLY,
                    MessageFormat.format("state:1,busno:{0}", busno));
            return;
        }
        billing.setUserid(userid);
        billing.setBtype(BillingType.BET.getType());
        AccountDO account = accountMapper.getAccountByUserid(userid);
        if (account == null) {
            log.error("用户投注，用户账户为空！");
            throw new RuntimeException("用户投注，用户账户为空！");
        }
        BigDecimal funds = BigDecimal.ZERO.add(account.getBalance()).add(account.getVamount()).add(account.getQuota());
        // 判断可用余额是否大于消费金额
        if (funds.compareTo(price) > -1) {
            price = processPrice(price, account.getVamount(), account.getVfrozen(), billing, ProcessType.VAMOUNT);
            price = processPrice(price, account.getBalance(), account.getBfrozen(), billing, ProcessType.BALANCE);
            price = processPrice(price, account.getQuota(), account.getQfrozen(), billing, ProcessType.QUOTA);
            Timestamp time = new Timestamp(System.currentTimeMillis());
            billing.setGmtcreate(time);
            billing.setGmtmodified(time);
            billingMapper.insert(billing);
            account.setBalance(billing.getBquantity());
            account.setVamount(billing.getVquantity());
            account.setQuota(billing.getQquantity());
            account.setBfrozen(billing.getBfrozen());
            account.setVfrozen(billing.getVfrozen());
            account.setQfrozen(billing.getQfrozen());
            account.setGmtcreate(account.getGmtmodified());
            account.setGmtmodified(time);
            if(accountMapper.updateAccount(account) != 1) {
                log.info("修改账户数据，版本过期。");
                throw new RuntimeException("修改账户数据，版本过期。");
            };
            
            double random = Math.random();
            System.out.println("=========================random:"+random);
            if(random > 0.3) {
                throw new RuntimeException("模拟保存账务明细，账号信息失败！！");
            }
            rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_FOR_BETTING, RabbitMQConst.ROUTE_FOR_SUCC_BETTING_REPLY,
                    MessageFormat.format("state:1,busno:{0}", busno));
        } else {
            // 订单金额大于可用金额，通知彩票端取消订单，NSF(余额不足not sufficient funds)
            rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_FOR_BETTING, RabbitMQConst.ROUTE_FOR_FAIL_NSF_BETTING_REPLY,
                    MessageFormat.format("state:0,busno:{0}", busno));
        }
    }

    private BigDecimal processPrice(BigDecimal price, BigDecimal fund, BigDecimal fundFrozen, BillingDO billing,
            ProcessType type) {
        // 优先使用购彩金，再本金，最后配额
        BigDecimal subtract = fund.subtract(price);
        BigDecimal volume = null;
        BigDecimal quantity = null;
        switch (BigDecimal.ZERO.compareTo(subtract)) {
        case 1:
            // 资金不够
            volume = fund;
            quantity = BigDecimal.ZERO;
            price = subtract.abs();
            break;
        case 0:
            // 消费金额等于资金
            volume = price;
            price = quantity = BigDecimal.ZERO;
            break;
        default:
            // 资金大于消费金额
            volume = price;
            quantity = subtract;
            price = BigDecimal.ZERO;
            break;
        }
        switch (type) {
        case VAMOUNT:
            // 彩金
            billing.setVvolume(volume.negate());
            billing.setVquantity(quantity);
            billing.setVfvolume(volume);
            billing.setVfrozen(volume.add(fundFrozen));
            break;
        case BALANCE:
            // 本金
            billing.setBvolume(volume.negate());
            billing.setBquantity(quantity);
            billing.setBfvolume(volume);
            billing.setBfrozen(volume.add(fundFrozen));
            break;
        case QUOTA:
            // 配额
            billing.setQvolume(volume.negate());
            billing.setQquantity(quantity);
            billing.setQfvolume(volume);
            billing.setQfrozen(volume.add(fundFrozen));
            break;
        }
        return price;
    }

    private enum ProcessType {
        BALANCE, VAMOUNT, QUOTA
    }
}
